#include "GameState.h"
#include "..\Game\TetrisBoard.h"
#include "..\Game\TetrisPiece.h"

using namespace Ogre;

GameState::GameState()
:	m_pDetailsPanel(NULL)
,	m_pCurrentPiece(NULL)
,	m_pNextPiece(NULL)
,	m_pTetrisBoard(NULL)
,	m_pMsgOnScreen(NULL)

,	m_TranslateVector(Vector3::ZERO)
,	m_CameraPos(Vector3(70, 50, 130))
,	m_CameraDir(70, 50, 0)
,	m_vPieceDir(Vector3(0.f, -1.f, 0.f))
,	m_vAdjustDir(Vector3::ZERO)

,	m_MoveSpeed(3.f)
,	m_RotateSpeed(4.f)
,	m_RotScale(0.f)
,	m_MoveScale(0.f)
,	m_fPieceSpeedX(3.f)	
,	m_fPieceSpeedY(MOD_PIECE_ACC_STEP)	

,	m_nLevel(1)
,	m_nScore(0)
,	m_nLines(0)
,	m_dwKeyRelease(0)

,	m_bLMouseDown(false) 
,	m_bRMouseDown(false)
,	m_bSettingsMode(false)
,	m_bQuit(false)	
,	m_bSpawnPiece(false)
,	m_bCanBreak(false)
,	m_bGameOver(false)	
{
	//Root::getSingleton().addFrameListener(this);
}

void GameState::CreateScene()
{
	m_pSceneMgr = AppFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_EXTERIOR_CLOSE, "GameSceneMgr");
	m_pSceneMgr->addRenderQueueListener(AppFramework::getSingleton().m_pOverlaySystem);
	//m_pSceneMgr->setAmbientLight(Ogre::ColourValue(1.f, 1.f, 1.f));
	m_pSceneMgr->createLight("LightLeft")->setPosition(0.f, 100.f, 50.f);  	
	m_pSceneMgr->createLight("LightRight")->setPosition(140.f, 100.f, 50.f); 
	g_pDebugDrawer = OGRE_NEW DebugDrawer(m_pSceneMgr, 0.5f);

	m_pCamera = m_pSceneMgr->createCamera("GameCamera");
	m_pCamera->setPosition(m_CameraPos);
	m_pCamera->lookAt(m_CameraDir);
	m_pCamera->setNearClipDistance(5);

	float width = AppFramework::getSingletonPtr()->m_pViewport->getActualWidth();
	float heigh = AppFramework::getSingletonPtr()->m_pViewport->getActualHeight();
	m_pCamera->setAspectRatio(width / heigh);
	AppFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);	
}

void GameState::Enter()
{
	AppFramework::getSingletonPtr()->m_pLog->logMessage("Entering GameState...");

	m_pTetrisBoard = OGRE_NEW cTetrisBoard();
	m_bSpawnPiece = true;
	m_bGameOver   = false;

	CreateScene();
	BuildGUI();

	CreateGrid(Vector3::ZERO, MOD_CUBE_SIZE, MOD_CUBE_SIZE * MOD_GRID_LINES, MOD_GRID_LINES, MOD_GRID_COLUMN);
	m_pMsgOnScreen = GetMovableText("score_id", "LEVEL\nSCORE", Vector3(130.f, 30.f, 0.f), MovableText::H_LEFT, MovableText::V_CENTER);
}

void GameState::Exit()
{
	AppFramework::getSingletonPtr()->m_pLog->logMessage("Leaving GameState...");	

	OGRE_SAFE_DELETE(g_pDebugDrawer);	
	OGRE_SAFE_DELETE(m_pTetrisBoard);
	OGRE_SAFE_DELETE(m_pCurrentPiece);
	if (m_pSceneMgr)
	{
		m_pSceneMgr->destroyCamera(m_pCamera);
		AppFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);
		m_pSceneMgr = NULL;
	}
}

void GameState::Update(double fDeltaTime)
{
    m_FrameEvent.timeSinceLastFrame = fDeltaTime;
    AppFramework::getSingletonPtr()->m_pTrayMgr->frameRenderingQueued(m_FrameEvent);

    if (m_bQuit == true)
    {
        popAppState();
        return;
    }

	if (m_bGameOver)
	{
		return;
	}

    m_MoveScale = m_MoveSpeed   * fDeltaTime;
    m_RotScale  = m_RotateSpeed * fDeltaTime;
    m_TranslateVector = Vector3::ZERO;
	MoveCamera();

	//char szDeltaTime[32];
	//sprintf(szDeltaTime, "fDeltaTime:%.4f\n", fDeltaTime);
	//OgreFramework::getSingletonPtr()->m_pLog->logMessage(szDeltaTime);	
	
	if (fDeltaTime >= 0.1f)
		fDeltaTime = 0.1f;

	DWORD dwFlagHor = 0;
	DWORD dwFlagVer = 0;
	if (m_pCurrentPiece) 
	{
		if (m_dwKeyRelease & (1 << 1))
		{
			m_vAdjustDir = Vector3::NEGATIVE_UNIT_X;
			m_pTetrisBoard->AdjustPiece(*m_pCurrentPiece, m_vAdjustDir);		
			m_dwKeyRelease &= (~(1 << 1));
		}
		if (m_dwKeyRelease & (1 << 2))
		{
			m_vAdjustDir = Vector3::UNIT_X;
			m_pTetrisBoard->AdjustPiece(*m_pCurrentPiece, m_vAdjustDir);			
			m_dwKeyRelease &= (~(1 << 2));					
		}
		if (m_vPieceDir.x != 0.f)
		{			
			Vector3 vPieceMove = Vector3(m_vPieceDir.x, 0.f, 0.f) * m_fPieceSpeedX * fDeltaTime;
			dwFlagHor = m_pTetrisBoard->GetCollissionType(*m_pCurrentPiece, vPieceMove); 
			if ((dwFlagHor & eFlagWallLeft) == false && (dwFlagHor & eFlagWallRight) == false && 
				(dwFlagHor & eFlagCubeLeft) == false && (dwFlagHor & eFlagCubeRight) == false)
			{
				m_pCurrentPiece->Update(0.f, Vector3::ZERO, 0.f, vPieceMove);
			}
		}
		if (m_vPieceDir.y != 0.f)
		{			
			Vector3 vPieceMove = Vector3(0.f,m_vPieceDir.y, 0.f) * m_fPieceSpeedY * fDeltaTime;
			dwFlagVer = m_pTetrisBoard->GetCollissionType(*m_pCurrentPiece, vPieceMove); 
			if ((dwFlagVer & eFlagWallDown) == false && (dwFlagVer & eFlagCubeDown) == false)
				m_pCurrentPiece->Update(0.f, Vector3::ZERO, 0.f, vPieceMove);
			else 
				m_bCanBreak = true;
		}

		if (m_bCanBreak )
		{				
			if (m_pTetrisBoard->BreakPiece(*m_pCurrentPiece, m_vAdjustDir))
			{
				Score(m_pTetrisBoard->DeleteLines(m_pSceneMgr));
				OGRE_SAFE_DELETE(m_pCurrentPiece);
				m_pCurrentPiece = NULL;
				m_vPieceDir.x	= 0.f;
				m_vAdjustDir	= Vector3::ZERO;
				m_bSpawnPiece	= true;
				m_bCanBreak	= false;
			}
			else
			{
				m_bGameOver = true;
				AppFramework::getSingletonPtr()->m_pTrayMgr->showYesNoDialog("Restart?", "Game Over");
			}
		}
	}

	if (m_bSpawnPiece)	
	{
		int random;
		random = (int)Math::RangeRandom(PIECE_NONE + 1, PIECE_TOTAL - 0.1f);
		if (m_pNextPiece == NULL)
		{
			m_pCurrentPiece = OGRE_NEW cTetrisPiece(m_pSceneMgr, (eTetrisPiece)random, 0.f);
			random = (int)Math::RangeRandom(PIECE_NONE + 1, PIECE_TOTAL - 0.1f);
			m_pNextPiece	= OGRE_NEW cTetrisPiece(m_pSceneMgr, (eTetrisPiece)random, 0.f);
			m_pNextPiece->SetPosition(Vector3(130.f, 50.f, 0.f));
		}
		else
		{
			m_pCurrentPiece = m_pNextPiece;
			random = (int)Math::RangeRandom(PIECE_NONE + 1, PIECE_TOTAL - 0.1f);
			m_pNextPiece	= OGRE_NEW cTetrisPiece(m_pSceneMgr, (eTetrisPiece)random, 0.f);
			m_pCurrentPiece->SetPosition(m_pCurrentPiece->GetInitialPos());
			//m_pCurrentPiece->SetPosition(Vector3(130.f, 50.f, 0.f));
			m_pNextPiece->SetPosition(Vector3(130.f, 50.f, 0.f));			
		}
		m_bSpawnPiece   = false;
	}	

	if (m_pMsgOnScreen)
	{	
		char szText[64];
		sprintf(szText, "LEVEL: %d\nSCORE: %d\n LINES: %d", m_nLevel, m_nScore, m_nLines);			
		String szMsg(szText);
		m_pMsgOnScreen->setCaption(szMsg);	
	}
}

bool GameState::Pause()
{
	AppFramework::getSingletonPtr()->m_pLog->logMessage("Pausing GameState...");
	return true;
}

void GameState::Resume()
{
	AppFramework::getSingletonPtr()->m_pLog->logMessage("Resuming GameState...");
	BuildGUI();
	AppFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
	m_bQuit = false;
}

void GameState::Restart()
{
	AppFramework::getSingletonPtr()->m_pLog->logMessage("Restarting Game...");

	m_nLevel = 1;
	m_nScore = 0;
	m_nLines = 0;

	m_pTetrisBoard->ClearAll(m_pSceneMgr);
	if (m_pCurrentPiece)
	{
		ClearPiece(*m_pCurrentPiece);
		OGRE_SAFE_DELETE(m_pCurrentPiece);
	}
	if (m_pNextPiece)
	{
		ClearPiece(*m_pNextPiece);
		OGRE_SAFE_DELETE(m_pNextPiece);
	}
	m_bSpawnPiece = true;
	m_bCanBreak = false;
	m_bGameOver = false;
	m_vPieceDir = Vector3(0.f, -1.f, 0.f);
}

bool GameState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
	if (AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_ESCAPE) && !m_bGameOver)
	{
		pushAppState(findByName("PauseState"));               
		//m_bQuit = true;
		return true;
	}    
	if (AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_1))
		m_pCamera->setPolygonMode(Ogre::PM_WIREFRAME);
	if (AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_2))
		m_pCamera->setPolygonMode(Ogre::PM_SOLID);
	if (AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_R))
	{
		m_pCamera->setPosition(m_CameraPos);
		m_pCamera->lookAt(m_CameraDir);
	}

	switch  (keyEventRef.key)
	{
	case OIS::KC_SPACE:
		{
			if (m_pCurrentPiece && m_pCurrentPiece->GetParentNode())
			{	
				if (m_pTetrisBoard->CanRotatePiece(*m_pCurrentPiece, Vector3::UNIT_Z, Ogre::Radian(-Math::PI * 0.5f)))
					m_pCurrentPiece->GetParentNode()->rotate(Vector3::UNIT_Z, Ogre::Radian(-Math::PI * 0.5f));
			}
			break;
		}
	case OIS::KC_DELETE:
		{
			//m_pTetrisBoard->BreakPiece(*m_pCurrentPiece);
			break;			
		}
	case OIS::KC_LEFT:
		{
			m_dwKeyRelease &= (~(1 << 1));
			m_vPieceDir.x = -1.f;			
			break;
		}
	case OIS::KC_RIGHT:
		{
			m_dwKeyRelease &= (~(1 << 2));
			m_vPieceDir.x = 1.f;			
			break;
		}
#ifdef _DEBUG
	case OIS::KC_UP:
		{
			//m_vPieceDir.y = 1.f;			
			break;
		}

#endif //_DEBUG
	case OIS::KC_DOWN:
		{
			m_fPieceSpeedY = MOD_PIECE_ACC; 
			m_vPieceDir.y  = -1.f;			
			break;
		}
	}
	return true;
}

bool GameState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
	switch  (keyEventRef.key)
	{
	case OIS::KC_LEFT:
		{
			//m_vAdjustDir = Vector3::NEGATIVE_UNIT_X;
			//m_pTetrisBoard->AdjustPiece(*m_pCurrentPiece, m_vAdjustDir);
			m_dwKeyRelease |= (1 << 1);
			m_vPieceDir.x = 0.f;						
			break;
		}
	case OIS::KC_RIGHT:
		{
			//m_vAdjustDir = Vector3::UNIT_X;
			//m_pTetrisBoard->AdjustPiece(*m_pCurrentPiece, m_vAdjustDir);			
			m_dwKeyRelease |= (1 << 2);
			m_vPieceDir.x = 0.f;			
			break;
		}
#ifdef _DEBUG
	case OIS::KC_UP:
		{
			//m_vPieceDir.y = 0.f;			
			break;
		}
#endif //_DEBUG
	case OIS::KC_DOWN:
		{
			m_fPieceSpeedY = MOD_PIECE_ACC_STEP * m_nLevel; 
			break;
		}
	}
	return true;
}

bool GameState::mouseMoved(const OIS::MouseEvent &evt)
{
	if (AppFramework::getSingletonPtr()->m_pTrayMgr->injectMouseMove(evt)) return true;

	if (m_bRMouseDown)
	{
		m_pCamera->yaw(Degree(evt.state.X.rel * -0.1f));
		m_pCamera->pitch(Degree(evt.state.Y.rel * -0.1f));
	}
	return true;
}

bool GameState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	if (AppFramework::getSingletonPtr()->m_pTrayMgr->injectMouseDown(evt, id))
		return true;

	if (id == OIS::MB_Left)
	{
		m_bLMouseDown = true;
	}
	else if (id == OIS::MB_Right)
	{
		m_bRMouseDown = true;
	}

	return true;
}

bool GameState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	if (AppFramework::getSingletonPtr()->m_pTrayMgr->injectMouseUp(evt, id))
		return true;
	if (id == OIS::MB_Left)
	{
		m_bLMouseDown = false;
	}
	else if (id == OIS::MB_Right)
	{
		m_bRMouseDown = false;
	}
	return true;
}

void GameState::yesNoDialogClosed(const Ogre::DisplayString& question, bool yesHit)
{
    if (yesHit == true)
		Restart();
    else
	{
		Exit();
		pushAppState(findByName("MenuState"));   
	}
	AppFramework::getSingletonPtr()->m_pTrayMgr->closeDialog();	
}

bool GameState::frameStarted(const Ogre::FrameEvent& evt)
{
	return true;
}

bool GameState::frameEnded(const Ogre::FrameEvent& evt)
{
	return true;
}

void GameState::CreateGrid(const Ogre::Vector3& pos, float step, float length, int nColumns, int nLines)
{
	CreateMaterial("black_line_mat", "line_group", Ogre::ColourValue(0.f, 0.f, 0.f, 1.f));
	for (int i = 0; i <= nLines; i++)
		Create3DLine("black_line_mat", Vector3(0, i * step, 0), Vector3(length, i * step, 0));	
	for (int i = 0; i <= nColumns; i++)
		Create3DLine("black_line_mat", Vector3(i * step, 0, 0), Vector3(i * step, length, 0));						
}

void GameState::CreateMaterial(const char* szName, const char* szResGroup, const Ogre::ColourValue& color)
{
	if (ResourceGroupManager::getSingleton().resourceGroupExists(szResGroup) == false)
		ResourceGroupManager::getSingleton().createResourceGroup(szResGroup);

	MaterialPtr pMaterial = MaterialManager::getSingleton().create(szName, szResGroup);
	pMaterial->setReceiveShadows(false);
	pMaterial->getTechnique(0)->setLightingEnabled(true);
	pMaterial->getTechnique(0)->getPass(0)->setDiffuse(color);
	pMaterial->getTechnique(0)->getPass(0)->setAmbient(color.r, color.g, color.b);
	pMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(color.r, color.g, color.b);
}

void GameState::Create3DLine(const char* szMaterial, const Ogre::Vector3& startPos, const Ogre::Vector3& endPos)
{
	static int nStaticIdx = 0;
	char szMsg[32];
	sprintf(szMsg, "line_%d", nStaticIdx);

	Ogre::ManualObject*	pLineObj = m_pSceneMgr->createManualObject(szMsg);	
	pLineObj->begin(szMaterial, RenderOperation::OT_LINE_LIST);
	pLineObj->position(startPos);
	pLineObj->position(endPos);
	pLineObj->end();

	sprintf(szMsg, "line_node_%d", nStaticIdx++);
	SceneNode* pNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode(szMsg);
	pNode->setPosition(Vector3::ZERO);
	pNode->attachObject(pLineObj);	
}

Ogre::MovableText*	GameState::GetMovableText(	const char* szId, const char* szText, const Ogre::Vector3& pos, Ogre::MovableText::HorAlign alignX,	Ogre::MovableText::VerAlign alignY)
{
	char szMsg[16];
	sprintf(szMsg, "Node_%s", szId);
	SceneNode* pNodeText;
	pNodeText = m_pSceneMgr->getRootSceneNode()->createChildSceneNode(szMsg);
	sprintf(szMsg, "Text_%s", szId);	
	MovableText* pMsg3D = OGRE_NEW MovableText(szMsg, szText, "SdkTrays/Value");
	pMsg3D->setTextAlignment(MovableText::H_CENTER, MovableText::V_ABOVE); 
	pMsg3D->setCharacterHeight(5);
	pMsg3D->setColor(ColourValue(0.f, 0.f, 0.f, 1.f));
	pNodeText->setPosition(pos);
	pNodeText->attachObject(pMsg3D);	

	return pMsg3D;
}

void GameState::MoveCamera()
{
	if(m_bSettingsMode == false)
	{
		if(AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_A))
			m_TranslateVector.x = -m_MoveScale;

		if(AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_D))
			m_TranslateVector.x = m_MoveScale;

		if(AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_W))
			m_TranslateVector.z = -m_MoveScale;

		if(AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_S))
			m_TranslateVector.z = m_MoveScale;
	}
	if (AppFramework::getSingletonPtr()->m_inputContext.mKeyboard->isKeyDown(OIS::KC_LSHIFT))
		m_pCamera->moveRelative(m_TranslateVector);
	m_pCamera->moveRelative(m_TranslateVector / 10);
}

void GameState::BuildGUI()
{
	AppFramework::getSingletonPtr()->m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMRIGHT);

	StringVector	items;
	DisplayString	szDisplay;
	items.push_back("Move");
	items.push_back("Move");
	items.push_back("Rotate");
	items.push_back("Speed");
	items.push_back("Camera");
	items.push_back("Camera Reset");
	items.push_back("Camera Rotate");
	items.push_back("UN/PAUSE");

	m_pDetailsPanel = AppFramework::getSingletonPtr()->m_pTrayMgr->createParamsPanel(OgreBites::TL_TOPRIGHT, "DetailsPanel", 250, items);
	m_pDetailsPanel->show();  

	szDisplay = "Left Arrow";
	m_pDetailsPanel->setParamValue(0, szDisplay);
	szDisplay = "Right Arrow";
	m_pDetailsPanel->setParamValue(1, szDisplay);
	szDisplay = "Space";
	m_pDetailsPanel->setParamValue(2, szDisplay);
	szDisplay = "Down Arrow";
	m_pDetailsPanel->setParamValue(3, szDisplay);
	szDisplay = "Shift + WASD";
	m_pDetailsPanel->setParamValue(4, szDisplay);
	szDisplay = "R";
	m_pDetailsPanel->setParamValue(5, szDisplay);
	szDisplay = "RClick + WASD";
	m_pDetailsPanel->setParamValue(6, szDisplay);
	szDisplay = "ESC";
	m_pDetailsPanel->setParamValue(7, szDisplay);
}

void GameState::ClearPiece(cTetrisPiece& piece)
{
	if (!piece.GetParentNode())
		return;

	Node::ChildNodeIterator iter = piece.GetParentNode()->getChildIterator();
	SceneNode* pNodeCube = NULL;
	String szName;
	while(iter.hasMoreElements()) 
	{
		pNodeCube	= (SceneNode*)iter.current()->second;
		szName		= pNodeCube->getName();
		iter.getNext();
		m_pSceneMgr->destroySceneNode(szName);
	}			
}

void GameState::Score(int lines)
{
	int bonus = m_nLevel;
	for (int i = 0; i < lines; ++i)
	{
		m_nScore += bonus;
		m_nLines++;
		bonus *= 2;
		if (m_nLines % 10 == 0)
			m_nLevel++;
	}
}